import {getLevel} from "../../database/models/player";
import {autoSerialize, serialize} from "../serializeDecorator"
import Card from "./card"
import PlayerState from "./playerAgent"
import Table from "./table"
import {DumpPhase, SubPhaseName, TableSubPhaseBase} from "./tablesubphases/base";
import {GameOverPhase} from "./tablesubphases/GameOverPhase";
import {QuizPhase} from "./tablesubphases/QuizPhase";
import {DelayPhase} from "./tablesubphases/DelayPhase";
import ms = require("ms");


type JiaoFenStatus = {
  currentIndex: number
  jiaoFens: { fen: number, guo: boolean }[],
  lowestFen: number,
}

type QiangDiZhuStatus = {
  currentIndex: number
  firstQiang: number
  diZhu: number
  qiangs: { buQiang: boolean, qiang: number, selected: boolean }[],
}

type SurrenderState = { index: number, zhuang: boolean, state: 'originator' | 'agree' | 'waiting' | 'disagree' }

type Quiz = {
  question: string, answer: string, wrongAnswers: string[]
  options: string[]
}

type PlayersAnswerState = { isFirstRight: boolean, right: boolean, answerAt: number, quit: boolean, answered: boolean }[]

export interface ITableHandler {


  onPlayerSubmitAnswer(player: PlayerState, answer)

  onPlayerRequestHint(player: PlayerState)
}

export default class NormalTable extends Table implements ITableHandler {
  @autoSerialize
  jiaoFenStatus: JiaoFenStatus = {
    currentIndex: 0,
    jiaoFens: [],
    lowestFen: 1
  }

  @autoSerialize
  qiangDiZhuStatus: QiangDiZhuStatus = {
    firstQiang: -1,
    qiangs: [],
    currentIndex: 0,
    diZhu: -1
  }

  @autoSerialize
  mingPlayerIndex: number[] = []

  @autoSerialize
  jiaoFen: number = 1;


  @autoSerialize
  lastSurrenderAt: number = 0

  @autoSerialize
  surrenderState: SurrenderState[] = []

  @autoSerialize
  surrenderTimeout: number = 0


  @autoSerialize
  selectPrimaryStartAt: number

  @autoSerialize
  state: 'quizStart' | 'quizOver' | 'gameStarted' | 'gameOver'

  @autoSerialize
  quizzes: Quiz[]

  @autoSerialize
  currentQuizIndex: number = 0


  @autoSerialize
  answerState: PlayersAnswerState

  @autoSerialize
  answerHistory: PlayersAnswerState [] = []

  @autoSerialize
  quizStartAt: number = 0

  quizStartTimeout: number = ms('10s');

  @autoSerialize
  quizOverAt: number = 0


  name() {
    return "douDiZhu"
  }

  phases: TableSubPhaseBase[] = [
    new DelayPhase(this, ms('4s')),
    new QuizPhase(this),
    new GameOverPhase(this),
    new DumpPhase(this)
  ]

  @autoSerialize
  currentPhaseName: SubPhaseName

  @serialize
  currentPhase: TableSubPhaseBase = null

  constructor(room, rule, restJushu) {

    super(room, rule, restJushu)

  }

  getCurrentQuizData(): { quiz: Quiz, index: number, total: number } {
    return {quiz: this.quizzes[this.currentQuizIndex], index: this.currentQuizIndex, total: this.quizzes.length}
  }

  isRightAnswer(index: number) {

    const quizData = this.getCurrentQuizData()

    const answer = quizData.quiz.options[index]

    return quizData.quiz.answer === answer
  }


  private initQuizzes() {

    this.quizzes = this.rule.quizzes.map(qz => {

      const options = [qz.answer, ...qz.wrongAnswers].sort()

      return {
        ...qz,
        options
      }
    })
  }

  start() {

    this.initQuizzes()

    this.players.map((p) => {
      p.index = this.atIndex(p)
    })

    this.startPhase()
  }

  onPlayerSubmitAnswer(player: PlayerState, answer: { index: number }) {
    this.currentPhase.onPlayerSubmitAnswer(player, answer)
  }

  startPhase() {
    const p = this.phases[0]
    this.currentPhaseName = p.name()
    this.currentPhase = p
    p.onEntry()
  }

  nextPhase() {
    const i = this.phases.findIndex(p => p.name() === this.currentPhaseName)

    if (i < 0) return;

    const currPhase = this.phases[i]
    let nextPhase = this.phases[i + 1]

    currPhase.onLeave()

    if (currPhase.name() === 'QuizPhase' && this.quizLeft() > 0) {

      nextPhase = currPhase
    }
    if (nextPhase) {
      this.currentPhaseName = nextPhase.name()
      nextPhase.onEntry()
      this.currentPhase = nextPhase
    } else {
      this.currentPhaseName = 'dumpGuardPhase'
    }
  }

  skipToPhase(toPhaseName: SubPhaseName) {
    const i = this.phases.findIndex(p => p.name() === this.currentPhaseName)

    if (i < 0) return;

    const toIndex = this.phases.findIndex(p => p.name() === toPhaseName)
    const currPhase = this.phases[i]
    const nextPhase = this.phases[toIndex]

    currPhase.onLeave()

    if (nextPhase) {
      this.currentPhaseName = nextPhase.name()
      nextPhase.onEntry()
      this.currentPhase = nextPhase
    } else {
      this.currentPhaseName = 'dumpGuardPhase'
    }
  }


  resume(json) {
    super.resume(json)

    this.diCards = this.diCards.map(c => Card.from(c))
    if (json.currentPhaseName) {
      this.skipToPhase(this.currentPhaseName)
      this.currentPhase.resume(json.currentPhase)
    }
  }

  toJSON() {
    if (this.state === 'gameOver') return null
    return super.toJSON()
  }

  listenPlayer(player) {
    super.listenPlayer(player)

    player.msgDispatcher.on('game/submitAnswer', (msg) => {
      this.onPlayerSubmitAnswer(player, msg)
    })
  }

  reconnectContent(index, reconnectPlayer: PlayerState) {
    const status = this.players.map((player, index) => {
      return {
        index,
        model: player.model,
        answerIndex: player.myAnswerIndex,
        isRight: player.myAnswerIsRight,
        score: player.score,
        answered: this.answerState && this.answerState[index].answered
      }
    })

    const quizData = this.getCurrentQuizData()

    const currentQuizData = {
      question: quizData.quiz.question,
      options: quizData.quiz.options,
      current: quizData.index, total: quizData.total,
      end: this.quizStartAt + this.quizStartTimeout
    }

    return {
      currentQuizData,
      tableState: this.state || '',
      answerState: this.answerState && this.answerState.map(({answered, quit}) => ({answered, quit})),
      status
    }
  }

  private isDrawGame() {
    const p = this.players[0]
    this.players.every(player => p.score === player.score)
  }

  private levelToPrize(level: number) {

    if (level === 0) {
      return 5
    } else if (level === 1) {
      return 20
    } else {
      return 50
    }
  }

  gameOver() {
    this.state = 'gameOver'

    const sortStates = this.players.slice().map(x => x).sort((x, y) => y.score - x.score)
    let winIndex = sortStates[0].score > 0 && sortStates[0].index

    const states = this.players.map(p => {

      const level = getLevel(p.model)

      const winPrize = this.levelToPrize(level)

      if (this.isDrawGame() || !this.rule.isPublic) {
        return {
          model: p.model,
          index: p.index,
          score: p.score,
          prize: 0
        }
      } else {
        return {
          model: p.model,
          index: p.index,
          score: p.score,
          prize: winIndex === p.index ? winPrize : -Math.round(winPrize * 0.6)
        }
      }
    })

    const gameOverMsg = {
      states,
      winIndex,
      juShu: this.restJushu,
      isPublic: this.room.isPublic,
      ruleType: this.rule.ruleType,
      juIndex: this.room.game.juIndex,
    }

    this.room.broadcast('game/game-over', gameOverMsg)
    this.stateData.gameOver = gameOverMsg

    const firstPlayer = this.players[0]

    this.roomGameOver(states, firstPlayer._id)
  }

  onPlayerRequestHint(player: PlayerState) {
  }

  initAnswerState() {
    this.answerState = this.players.map(() => {
      return {
        right: false, answered: false, quit: false, answerAt: 0,
        isFirstRight: false
      }
    })
  }

  initPlayerState() {
    this.players.map((p) => {
      p.myAnswerIndex = -1
      p.myAnswerIsRight = false
    })
  }

  recordPlayerAnswer(player: PlayerState, answer: { index: number }) {
    const index = this.atIndex(player)

    if (this.isFistSubmitAnswered()) {
      this.answerState[index].isFirstRight = true
    }

    this.answerState[index].answered = true
    this.answerState[index].answerAt = Date.now()
    this.answerState[index].right = this.isRightAnswer(answer.index)
  }

  recordPlayerQuit(player: PlayerState) {
    const index = this.atIndex(player)
    this.answerState[index].quit = true
    this.answerState[index].answered = true
  }

  isAllAnswered() {
    return this.answerState.every(({answered}) => answered)
  }

  showAllAnswerInfo() {
    let allAnswerData = this.players.map(p => {
      return {
        index: p.index,
        model: p.model,
        answerIndex: p.myAnswerIndex,
        isRight: p.myAnswerIsRight
      }
    })
    this.room.broadcast('game/quizAllAnswer', allAnswerData)
  }

  isFistSubmitAnswered() {
    return this.answerState.every(({answered}) => !answered)
  }


  isAnswered(player: PlayerState) {
    const i = this.atIndex(player)
    return this.answerState[i].answered
  }

  nextQuiz() {
    this.answerHistory.push(this.answerState)
    this.currentQuizIndex += 1
  }

  quizLeft(): number {
    return this.quizzes.length - this.currentQuizIndex
  }

  private isAllSubmitRightAnswer() {
    return this.answerState.every(({right}) => right)
  }

  currentQuizScore(p: PlayerState) {
    const index = this.atIndex(p)

    let score = 0

    const currentQuizIndex = this.currentQuizIndex
    const firstWinScore = [160, 180, 200, 220, 400]
    const winScore = [100, 110, 120, 130, 220]

    if (this.isAllSubmitRightAnswer()) {
      score = winScore[currentQuizIndex]
      if (this.answerState[index].isFirstRight) {
        score = firstWinScore[currentQuizIndex]
      }
    } else {
      if (this.answerState[index].right) {
        score = firstWinScore[currentQuizIndex]
      }
    }

    return score
  }
};
